object TestFunctionDeclare {

  def main(args: Array[String]): Unit = {

    // 函数1：无参，无返回值
    def test1(): Unit = {
      println("无参，无返回值")
    }

    test1()

    // 函数2：无参，有返回值
    def test2(): String = {
      return "无参，有返回值"
    }

    println(test2())

    // 函数3：有参，无返回值
    def test3(s: String): Unit = {
      println(s)
    }

    test3("jinlian")

    // 函数4：有参，有返回值
    def test4(s: String): String = {
      return s + "有参，有返回值"
    }

    println(test4("hello "))


    // 函数5：多参，无返回值
    def test5(name: String, age: Int): Unit = {
      println(s"$name, $age")
    }

    test5("dalang", 40)


    /**
     * 5.1.4 函数参数
     * （1）可变参数
     * （2）如果参数列表中存在多个参数，那么可变参数一般放置在最后
     * （3）参数默认值，一般将有默认值的参数放置在参数列表的后面
     * （4）带名参数
     * */
    // （1）可变参数
    def test10(s: String*): Unit = {
      println(s)
    }

    // 有输入参数：输出 Array
    test10("Hello", "Scala")

    // 无输入参数：输出List()
    test10()

    // (2)如果参数列表中存在多个参数，那么可变参数一般放置在最后
    def test11(name: String, s: String*): Unit = {
      println(name + "," + s)
    }

    test11("jinlian", "dalang")

    // (3)参数默认值
    def test12(name: String, age: Int = 30): Unit = {
      println(s"$name, $age")
    }
    // 如果参数传递了值，那么会覆盖默认值
    test12("jinlian", 20)
    // 如果参数有默认值，在调用的时候，可以省略这个参数
    test12("dalang")

    // 一般情况下，将有默认值的参数放置在参数列表的后面
    def test13(sex: String = "男", name: String): Unit =
      println(s"$name, $sex")

    // Scala函数中参数传递是，从左到右
    //test13("wusong")
    //（4）带名参数
    test13(name = "ximenqing")

    /**
     * @ 5.1.5 函数至简原则（重点）
     * （1）return 可以省略，Scala会使用函数体的最后一行代码作为返回值
     * （2）如果函数体只有一行代码，可以省略花括号
     * （3）返回值类型如果能够推断出来，那么可以省略（:和返回值类型一起省略）
     * （4）如果有return，则不能省略返回值类型，必须指定
     * （5）如果函数明确声明unit，那么即使函数体中使用return关键字 也不起作用
     * （6）Scala 如果期望是无返回值类型，可以省略等号
     * （7）如果函数无参，但是声明了参数列表，那么调用时，小括号，可加可不加
     * （8）如果函数没有参数列表，那么小括号可以省略，调用时小括号必须省略
     * （9）如果不关心名称，只关心逻辑处理，那么函数名（def）可以省略
     * */
    // （0）函数标准写法
    def f(s: String): String = {
      return s + " jinlian"
    }

    println(f("Hello"))

    // 至简原则:能省则省

    //（1） return可以省略,Scala会使用函数体的最后一行代码作为返回值
    def f1(s: String): String = {
      s + " jinlian"
    }

    println(f1("Hello1"))

    //（2）如果函数体只有一行代码，可以省略花括号
    def f2(s: String): String = s + " jinlian"

    println(f2("Hello2"))

    //（3）返回值类型如果能够推断出来，那么可以省略（:和返回值类型一起省略）

    def f3(s: String) = s + " jinlian"

    println(f3("Hello3"))

    //（4）如果有return，则不能省略返回值类型，必须指定。
    def f4(): String = {
      return "ximenqing4"
    }

    println(f4())

    //（5）如果函数明确声明unit，那么即使函数体中使用return关键字也不起作用

    def f5(): Unit = {
      return "dalang5"
    }

    println(f5())

    //（6）Scala如果期望是无返回值类型,可以省略等号
    // 将无返回值的函数称之为过程
    def f6() {
      "dalang6"
    }

    println(f6())

    //（7）如果函数无参，但是声明了参数列表，那么调用时，小括号，可加可不加

    def f7() = "dalang7"

    println(f7())

    println(f7)

    //（8）如果函数没有参数列表，那么小括号可以省略,调用时小括号必须省略

    def f8 = "dalang"
    //println(f8())
    println(f8)

    //（9）如果不关心名称，只关心逻辑处理，那么函数名（def）可以省略
    def f9 = (x: String) => {
      println("wusong")
    }

    def f10(f: String => Unit) = {
      f("")
    }

    f10(f9)
    println(f10((x: String) => {
      println("wusong")
    }))

    /**
     * 1）函数可以作为值进行传递
     * */
    //（1）调用foo函数，把返回值给变量f
    //val f = foo()
    val function1 = foo()
    println(function1)

    //（2）在被调用函数foo后面加上 _，相当于把函数foo当成一个整体，传递给变量f1
    val function2 = foo _

    foo()
    function2()
    //（3）如果明确变量类型，那么不使用下划线也可以将函数作为整体传递给变量
    var function3: () => Int = foo
    function3()
  }

  def foo(): Int = {
    println("foo...")
    1
  }

  /**
   * 2）函数可以作为参数进行传递
   * */
  // （1）定义一个函数，函数参数还是一个函数签名；f表示函数名称;(Int,Int)表示输入两个Int参数；Int表示函数返回值
  def f11(f: (Int, Int) => Int): Int = {
    f(2, 4)
  }

  // （2）定义一个函数，参数和返回值类型和f1的输入参数一致
  def add(a: Int, b: Int): Int = a + b

  // （3）将add函数作为参数传递给f1函数，如果能够推断出来不是调用，_可以省略
  println(f11(add)) //可以传递匿名函数


  /** *
   * 3）函数可以作为函数返回值返回
   */
  def f1() = {
    def f2() = {

    }

    f2 _
  }

  val f = f1()
  // 因为f1函数的返回值依然为函数，所以可以变量f可以作为函数继续调用
  f()
  // 上面的代码可以简化为
  f1()()
}
